﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Diagnostics;
using CM = Microsoft.Practices.Mobile.ContainerModel;
using Microsoft.Practices.Mobile.ContainerModel;
using Microsoft.Practices.Mobile.ObjectBuilder;

namespace PerfStatsCS
{
	public partial class MainForm : Form
	{
		public MainForm()
		{
			InitializeComponent();
		}

		private long Measure(Action action, int iterations)
		{
			GC.Collect();
			var watch = Stopwatch.StartNew();

			for (int i = 0; i < iterations; i++)
			{
				action();
			}

			return watch.ElapsedTicks / iterations;
		}

		private void btnNoDI_Click(object sender, EventArgs e)
		{
			Action action = () =>
			{
				var app = new WebApp(
					new Authenticator(
						new Logger(),
						new ErrorHandler(
							new Logger()
						),
						new Database(
							new Logger(),
							new ErrorHandler(
								new Logger()
							)
						)
					),
					new StockQuote(
						new Logger(),
						new ErrorHandler(
							new Logger()
						),
						new Database(
							new Logger(),
							new ErrorHandler(
								new Logger()
							)
						)
					)
				);

				app.Run();
			};

			lblNoDI.Text = Measure(action, int.Parse(txtIterations.Text)).ToString();
		}

		private void btnAutofac_Click(object sender, EventArgs e)
		{
			var builder = new Autofac.Builder.ContainerBuilder();
			builder.Register<IWebApp>(
				c => new WebApp(
					c.Resolve<IAuthenticator>(), 
					c.Resolve<IStockQuote>()))
				.FactoryScoped();

			builder.Register<IAuthenticator>(
				c => new Authenticator(
					c.Resolve<ILogger>(),
					c.Resolve<IErrorHandler>(),
					c.Resolve<IDatabase>()))
				.FactoryScoped();

			builder.Register<IStockQuote>(
				c => new StockQuote(
					c.Resolve<ILogger>(),
					c.Resolve<IErrorHandler>(),
					c.Resolve<IDatabase>()))
				.FactoryScoped();

			builder.Register<IDatabase>(
				c => new Database(
					c.Resolve<ILogger>(),
					c.Resolve<IErrorHandler>()))
				.FactoryScoped();

			builder.Register<IErrorHandler>(
				c => new ErrorHandler(c.Resolve<ILogger>()))
				.FactoryScoped();

			builder.Register<ILogger>(c => new Logger());

			var container = builder.Build();

			Action action = () =>
			{
				var webApp = container.Resolve<IWebApp>();
				webApp.Run();
			};

			lblAutofac.Text = Measure(action, int.Parse(txtIterations.Text)).ToString();
		}

		private void btnNinject_Click(object sender, EventArgs e)
		{
			var kernel = new Ninject.Core.StandardKernel(new SampleModule());

			Action action = () =>
			{
				var webApp = kernel.Get<IWebApp>();
				webApp.Run();
			};

			lblNinject.Text = Measure(action, int.Parse(txtIterations.Text)).ToString();
		}

		class SampleModule : Ninject.Core.StandardModule
		{
			public override void Load()
			{
				Bind<IWebApp>().ToMethod(
					c => new WebApp(
						c.Kernel.Get<IAuthenticator>(),
						c.Kernel.Get<IStockQuote>()))
					.Using<Ninject.Core.Behavior.TransientBehavior>();

				Bind<IAuthenticator>().ToMethod(
					c => new Authenticator(
						c.Kernel.Get<ILogger>(),
						c.Kernel.Get<IErrorHandler>(),
						c.Kernel.Get<IDatabase>()))
					.Using<Ninject.Core.Behavior.TransientBehavior>();

				Bind<IStockQuote>().ToMethod(
					c => new StockQuote(
						c.Kernel.Get<ILogger>(),
						c.Kernel.Get<IErrorHandler>(),
						c.Kernel.Get<IDatabase>()))
					.Using<Ninject.Core.Behavior.TransientBehavior>();

				Bind<IDatabase>().ToMethod(
					c => new Database(
						c.Kernel.Get<ILogger>(),
						c.Kernel.Get<IErrorHandler>()))
					.Using<Ninject.Core.Behavior.TransientBehavior>();

				Bind<IErrorHandler>().ToMethod(
					c => new ErrorHandler(c.Kernel.Get<ILogger>()))
					.Using<Ninject.Core.Behavior.TransientBehavior>();

				Bind<ILogger>().ToMethod(c => new Logger())
					.Using<Ninject.Core.Behavior.TransientBehavior>();
			}
		}

		private void btnUnity_Click(object sender, EventArgs e)
		{
			var container = new Microsoft.Practices.Unity.UnityContainer();
			container.AddExtension(new FactoryDefaultLifetime());

			var builder = container
				.AddNewExtension<Microsoft.Practices.Unity.StaticFactory.StaticFactoryExtension>()
				.Configure<Microsoft.Practices.Unity.StaticFactory.IStaticFactoryConfiguration>();

			builder.RegisterFactory<IWebApp>(
				c => new WebApp(
					c.Resolve<IAuthenticator>(),
					c.Resolve<IStockQuote>()));

			builder.RegisterFactory<IAuthenticator>(
				c => new Authenticator(
					c.Resolve<ILogger>(),
					c.Resolve<IErrorHandler>(),
					c.Resolve<IDatabase>()));

			builder.RegisterFactory<IStockQuote>(
				c => new StockQuote(
					c.Resolve<ILogger>(),
					c.Resolve<IErrorHandler>(),
					c.Resolve<IDatabase>()));

			builder.RegisterFactory<IDatabase>(
				c => new Database(
					c.Resolve<ILogger>(),
					c.Resolve<IErrorHandler>()));

			builder.RegisterFactory<IErrorHandler>(
				c => new ErrorHandler(c.Resolve<ILogger>()));

			builder.RegisterFactory<ILogger>(c => new Logger());


			Action action = () =>
			{
				var webApp = container.Resolve<IWebApp>();
				webApp.Run();
			};

			lblUnity.Text = Measure(action, int.Parse(txtIterations.Text)).ToString();
		}

		// Unbelievable complex to do such a stupid thing...
		class FactoryDefaultLifetime : Microsoft.Practices.Unity.UnityContainerExtension
		{
			protected override void Initialize()
			{
				Context.Policies.SetDefault<Microsoft.Practices.ObjectBuilder2.ILifetimePolicy>(
					new Microsoft.Practices.ObjectBuilder2.TransientLifetimePolicy());
			}
		}

		private void btnCM_Click(object sender, EventArgs e)
		{
			var container = new CM.Container { DefaultReuse = ReuseScope.None };
			container.Register<IWebApp>(
				c => new WebApp(
					c.Resolve<IAuthenticator>(),
					c.Resolve<IStockQuote>()));

			container.Register<IAuthenticator>(
				c => new Authenticator(
					c.Resolve<ILogger>(),
					c.Resolve<IErrorHandler>(),
					c.Resolve<IDatabase>()));

			container.Register<IStockQuote>(
				c => new StockQuote(
					c.Resolve<ILogger>(),
					c.Resolve<IErrorHandler>(),
					c.Resolve<IDatabase>()));

			container.Register<IDatabase>(
				c => new Database(
					c.Resolve<ILogger>(),
					c.Resolve<IErrorHandler>()));

			container.Register<IErrorHandler>(
				c => new ErrorHandler(c.Resolve<ILogger>()));

			container.Register<ILogger>(c => new Logger());

			Action action = () =>
			{
				var webApp = container.Resolve<IWebApp>();
				webApp.Run();
			};

			lblContainerModel.Text = Measure(action, int.Parse(txtIterations.Text)).ToString();
		}

		#region ObjectBuilder
			
		private void btnObjectBuilder_Click(object sender, EventArgs e)
		{
			var builder = new Builder();
			var mappings = new TypeMappingPolicy(typeof(IWebApp), null);

			builder.Policies.Set<ITypeMappingPolicy>(new TypeMappingPolicy(typeof(WebApp), null), typeof(IWebApp), null);
			builder.Policies.Set<ITypeMappingPolicy>(new TypeMappingPolicy(typeof(Authenticator), null), typeof(IAuthenticator), null);
			builder.Policies.Set<ITypeMappingPolicy>(new TypeMappingPolicy(typeof(StockQuote), null), typeof(IStockQuote), null);
			builder.Policies.Set<ITypeMappingPolicy>(new TypeMappingPolicy(typeof(Database), null), typeof(IDatabase), null);
			builder.Policies.Set<ITypeMappingPolicy>(new TypeMappingPolicy(typeof(ErrorHandler), null), typeof(IErrorHandler), null);
			builder.Policies.Set<ITypeMappingPolicy>(new TypeMappingPolicy(typeof(Logger), null), typeof(ILogger), null);

			Action action = () =>
			{
				var webApp = builder.BuildUp<IWebApp>(new NullLocator(), null, null);
				webApp.Run();
			};

			lblObjectBuilder.Text = Measure(action, int.Parse(txtIterations.Text)).ToString();
		}

		public class SimpleBuilder : BuilderBase<BuilderStage>
		{
			public SimpleBuilder()
			{
				Strategies.AddNew<TypeMappingStrategy>(BuilderStage.PreCreation);
				Strategies.AddNew<ConstructorReflectionStrategy>(BuilderStage.PreCreation);
				Strategies.AddNew<CreationStrategy>(BuilderStage.Creation);

				Policies.SetDefault<ICreationPolicy>(new DefaultCreationPolicy());
			}
		}

		public class NullLocator : IReadWriteLocator
		{
			public void Add(object key, object value)
			{
			}

			public bool Remove(object key)
			{
				return false;
			}

			public bool Contains(object key, SearchMode options)
			{
				return false;
			}

			public bool Contains(object key)
			{
				return false;
			}

			public int Count
			{
				get { return 0; }
			}

			public IReadableLocator FindBy(SearchMode options, Predicate<KeyValuePair<object, object>> predicate)
			{
				return null;
			}

			public IReadableLocator FindBy(Predicate<KeyValuePair<object, object>> predicate)
			{
				return null;
			}

			public object Get(object key, SearchMode options)
			{
				return null;
			}

			public object Get(object key)
			{
				return null;
			}

			public TItem Get<TItem>(object key, SearchMode options)
			{
				return default(TItem);
			}

			public TItem Get<TItem>(object key)
			{
				return default(TItem);
			}

			public TItem Get<TItem>()
			{
				return default(TItem);
			}

			public IReadableLocator ParentLocator
			{
				get { return null; }
			}

			public bool ReadOnly
			{
				get { return false; }
			}

			public IEnumerator<KeyValuePair<object, object>> GetEnumerator()
			{
				return new Dictionary<object, object>().GetEnumerator();
			}

			System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
			{
				return GetEnumerator();
			}
		}

		#endregion
	}
}